2dspere index in {mongodb}
mongodb에서 $geoNear
를 사용하여 검색을 하기 위해선 반드시 2d
혹은 2dsphere
인덱스가 정해진 컬럼에 포함되어야 합니다.
아래 문서는 MongoDB에서 제공하는 2dsphere 인덱스(지구 표면을 근사하여 구면 상의 위치 정보를 지원하기 위한 인덱스)에 대해, 지리적 쿼리에 사용되는 $geoNear
명령(Aggregation Pipeline 단계)까지 포함해 한국어로 정리한 문서입니다. 공식 문서( MongoDB 공식 Geospatial Queries )를 바탕으로 이해하기 쉽게 재구성했습니다.
MongoDB 2dsphere 인덱스 개요
MongoDB는 지리 공간 쿼리(Geospatial Query)를 지원하기 위해 두 가지 종류의 지리 공간 인덱스를 제공하며, 그 중 하나가 2dsphere 인덱스입니다. 2dsphere 인덱스는 지구 표면(구 형태)을 모형화하여, GeoJSON 객체 또는 좌표 쌍으로 지정된 도형(점, 선, 다각형 등)에 대한 공간 연산을 효율적으로 처리할 수 있도록 지원합니다.
주요 특성은 아래와 같습니다:
-
구(球) 형태의 좌표 계산
- 2dsphere 인덱스는 “경도(Longitude), 위도(Latitude)”로 표현되는 WGS84(World Geodetic System 1984) 좌표계나 GeoJSON 좌표계를 사용합니다.
- 거리 계산 등에 실제 지구 구면을 기반으로 한 근사값을 활용합니다.
-
다양한 GeoJSON 형태 지원
Point
(점)LineString
(선)Polygon
(다각형)MultiPoint
,MultiLineString
,MultiPolygon
GeometryCollection
- 이러한 도형들 간의 관계(포함 여부, 교차 여부, 근접도 등)를 매우 빠르게 계산할 수 있습니다.
-
지리적 연산을 위한 전용 쿼리 연산자 제공
$near
,$geoWithin
,$geoIntersects
,$geoNear(집계 파이프라인)
등을 통해, 반경 내 위치 검색, 특정 영역(폴리곤 등)에 포함된 문서 검색, 도형 간 교차 여부 등을 빠르게 검색하고 계산할 수 있습니다.
2dsphere 인덱스 생성 방법
인덱스를 생성할 필드 구조
일반적으로 2dsphere 인덱스를 생성하려면, 해당 필드에는 GeoJSON 형태 또는 [경도, 위도] 배열 형식이 저장되어 있어야 합니다. 예시로 location
필드에 Point
타입의 GeoJSON을 저장한다고 하면 아래와 같은 형태가 일반적입니다:
{
"_id": 1,
"name": "Seoul City Hall",
"location": {
"type": "Point",
"coordinates": [126.9779692, 37.566535]
}
}
MongoDB에서 좌표는 [경도, 위도] 순서여야 합니다.
2dsphere 인덱스 생성 예시
location
필드를 2dsphere 인덱스로 생성하려면 아래 명령어를 사용합니다:
db.collection.createIndex(
{ location: "2dsphere" }
);
createIndex
메서드는 해당 컬렉션에 지정된 필드(여기서는location
)에 2dsphere 인덱스를 생성합니다.- 2dsphere 인덱스는 MongoDB 버전 2.4 이상에서 지원됩니다.
또한 다른 필드와 결합하여 복합 인덱스를 생성할 수도 있습니다. 예를 들어, category
필드와 함께 만들 수 있습니다:
db.collection.createIndex(
{ category: 1, location: "2dsphere" }
);
이렇게 하면 MongoDB가 category
로 먼저 정렬한 뒤, 동일한 범주 내에서 location
지리 쿼리를 효율적으로 수행할 수 있습니다.
2dsphere 인덱스를 활용한 쿼리
2dsphere 인덱스를 사용하면, MongoDB에서 제공하는 지리 공간 전용 연산자를 통해 지리적 연산을 빠르게 처리할 수 있습니다.
1. $near
- 기능: 특정 점(좌표)에서 가까운 순서대로 문서를 정렬하여 가져옵니다.
- 사용 상황: 반경 내 검색(원형 범위)이나 가장 가까운 순서 정렬이 필요한 경우.
예시) 서울시청(coordinates: [126.9779692, 37.566535]
)을 기준으로 2km 이내에 있는 문서를 검색하고, 가까운 순으로 정렬하는 쿼리:
db.collection.find({
location: {
$near: {
$geometry: {
type: "Point",
coordinates: [126.9779692, 37.566535]
},
$maxDistance: 2000 // 단위: 미터
}
}
});
$geometry
: 검색 기준점(GeoJSON 형태)$maxDistance
: 최대 검색 거리(미터 기준)$nearSphere
라는 연산자도 존재하지만, 2dsphere 인덱스에서는$near
가 이미 구면 계산을 지원합니다.
2. $geoWithin
- 기능: 지정한 다각형(Polygon), 멀티폴리곤(MultiPolygon), 원(Circle), 경계 상자(Box) 등의 도형 내에 포함된 문서를 찾습니다.
- 사용 상황: 특정 구역이나 행정 구역 내부에 해당하는 문서를 검색할 때.
예시) 아래는 [126.90, 37.55]
~ [127.10, 37.70]
범위의 사각형 내부에 위치한 문서를 검색하는 쿼리입니다:
db.collection.find({
location: {
$geoWithin: {
$geometry: {
type: "Polygon",
coordinates: [
[
[126.90, 37.55],
[127.10, 37.55],
[127.10, 37.70],
[126.90, 37.70],
[126.90, 37.55]
]
]
}
}
}
});
- 도형(Polygon)에서는 반드시 폐곡선(시작점과 끝점이 동일)으로 정의해야 합니다.
3. $geoIntersects
- 기능: 특정 도형이 다른 도형과 교차(Intersects) 하는 문서를 찾습니다.
- 사용 상황: 예를 들어, 사용자 정의 다각형이 컬렉션에 있는 폴리곤(예: 지역 경계)들과 중첩되는지 확인할 때.
예시) region
필드에 저장된 폴리곤이, 아래 정의된 폴리곤과 교차하는지 여부를 판별하는 쿼리:
db.collection.find({
region: {
$geoIntersects: {
$geometry: {
type: "Polygon",
coordinates: [
[
[126.90, 37.55],
[127.00, 37.55],
[127.00, 37.65],
[126.90, 37.65],
[126.90, 37.55]
]
]
}
}
}
});
$geoNear
(Aggregation Pipeline)
$geoNear
는 집계(Aggregation) 파이프라인에서 사용되는 지리 공간 연산 단계입니다. $near
쿼리와 유사하게 거리에 대한 계산 및 정렬을 수행하지만, 집계 파이프라인과 결합해 보다 복잡한 분석이나 후속 처리를 쉽게 구현할 수 있습니다.
사용 방법
db.collection.aggregate([
{
$geoNear: {
near: {
type: "Point",
coordinates: [126.9779692, 37.566535]
},
distanceField: "dist.calculated",
maxDistance: 2000, // 선택 사항 (미터 단위)
query: { category: "cafe" }, // 다른 필드에 대한 필터 조건 가능
spherical: true // 구면(지구) 계산 사용 여부
}
},
{
$project: {
name: 1,
"dist.calculated": 1
}
}
]);
$geoNear
단계는 파이프라인 첫 단계(또는$match
가 없는 첫 번째 단계)에서만 사용할 수 있습니다.- 주요 옵션:
near
: 기준점(GeoJSON 또는[경도, 위도]
배열)distanceField
: 거리 값을 저장할 필드 이름 지정maxDistance
: 최대 검색 거리(단위: 미터)query
: 다른 조건과 결합 가능 (예:category: "cafe"
)spherical
:true
로 설정 시 지구 구면을 근사하여 거리 계산
이렇게 $geoNear
를 활용하면, 거리 정보를 계산해 문서에 추가한 뒤, 후속 $match
, $group
, $project
등의 파이프라인 스테이지로 다양한 분석을 수행할 수 있습니다.
주의사항 및 베스트 프랙티스
- 인덱스 생성 시 좌표 순서 준수
[경도, 위도]
순서를 잘못 지정하면 의도와 다른 결과가 나올 수 있습니다.
- 필드 데이터 형식
location.type
이Point
,Polygon
등 유효한 GeoJSON 타입인지 확인합니다.coordinates
배열도 올바른 구조를 갖춰야 합니다. (특히 Polygon의 경우 폐곡선으로 정의)
- 복합 인덱스 사용 시 고려 사항
- 지리 공간 인덱스와 일반(1, -1) 인덱스를 함께 쓸 때는 쿼리 패턴을 주의 깊게 살펴봐야 합니다.
- 예를 들어
{ category: 1, location: "2dsphere" }
를 만들면, 쿼리 시category
를 먼저 선별한 뒤location
지리 쿼리를 수행합니다. - 지리 연산과 범위 쿼리가 함께 있을 때는 쿼리 계획(Explain)을 통해 성능을 점검해야 합니다.
- 지리적 범위 제한(쿼리 스캔 최소화)
$near
혹은$geoNear
사용 시,$maxDistance
혹은maxDistance
를 설정하지 않으면 스캔 범위가 커져 성능 저하가 발생할 수 있습니다.- 가능한 구체적인 반경 또는 쿼리 조건을 설정하여 효율을 높이는 것이 좋습니다.
- 정확도 vs 성능
- 2dsphere 인덱스는 실제 GIS(지리 정보 시스템)에 비해 오차 범위가 있는 근사 계산을 합니다.
- 그러나 대부분의 위치 기반 서비스(O2O, 지도, 매장 찾기 등)에는 충분한 정확도를 제공하므로, 요구사항에 맞추어 사용하면 됩니다.
예시 시나리오
-
매장 위치 검색
- 사용자 현재 위치에서 가장 가까운 매장 10곳 찾기
$near
또는$geoNear
사용 +$limit
설정
-
택시/배달 서비스
- 드라이버(또는 라이더)의 위치를 실시간으로 저장하고, 사용자 주변 드라이버를 검색
$geoWithin
으로 구역 기반 필터,$near
또는$geoNear
로 거리 기반 정렬
-
지역 경계 기반 통계
- Polygon으로 행정 구역을 저장하고,
$geoWithin
을 통해 사용자가 속한 행정 구역을 판별 $geoIntersects
로 교차 여부 분석(예: 이벤트 반경과 행정 구역이 얼마나 겹치는지)
- Polygon으로 행정 구역을 저장하고,
-
데이터 후처리/분석
$geoNear
를 Aggregation Pipeline에 결합하여, 검색된 데이터에 대한 추가 통계나 그룹 연산을 수행
결론
MongoDB의 2dsphere 인덱스는 지구 표면을 다루는 다양한 위치 기반 애플리케이션에서 핵심적으로 사용됩니다.
- 핵심 요약:
location
필드를 GeoJSON 형태(혹은 경도/위도 배열)로 설정db.collection.createIndex({ location: "2dsphere" })
로 인덱스 생성$near
,$geoWithin
,$geoIntersects
,$geoNear
등을 활용하여 지리 쿼리를 효율적으로 수행
복잡한 분석이 필요한 경우 $geoNear
단계와 Aggregation Pipeline을 결합해 다양하게 확장할 수 있습니다. 실무에서는 데이터 분포와 실제 쿼리 패턴에 따라 지리 인덱스를 어떻게 설계할지, 쿼리를 어떻게 최적화할지를 고려하는 것이 중요합니다.
참고 자료
- MongoDB 공식 문서: Geospatial Queries
위 문서는 MongoDB 2dsphere 인덱스 및 $geoNear
를 포함한 지리 공간 쿼리에 대한 핵심 개념과 사용법을 정리한 것으로, 실제 프로젝트 적용 시에는 더 상세한 데이터 모델링, 샤딩, 성능 튜닝 전략 등을 함께 고려하시기 바랍니다.